#include "mylog.hpp"

#include <string>
#include <iostream>
#include <chrono>
#include <ctime>
#include <fstream>
#include <filesystem>

void mylog::info(std::string message) {
	get().infoImpl(message);
}
void mylog::note(std::string message) {
	get().noteImpl(message);	
}
void mylog::warning(std::string message) {
	get().warningImpl(message);
}
void mylog::error(std::string message, int errorCode) {
	get().errorImpl(message, errorCode);
}
void mylog::newLogFile(std::string fileName) {
	get().newLogFileImpl(fileName);
}
void mylog::changeLogLevel(std::uint_fast8_t newLevel) {
	get().changeLogLevelImpl(newLevel);
}
bool mylog::removeLogFile() {
	return get().removeLogFileImpl();
}
int mylog::getLogLevel() {
	return get().getLogLevelImpl();
}

mylog& mylog::get() {
	static mylog instance;
	return instance;
}

mylog::mylog() {}

void mylog::infoImpl(std::string message) {
	if(this->logLevel <= 3) {
		std::string finalMessage;
		auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
		finalMessage = std::string(std::ctime(&time)).substr(0, std::string(std::ctime(&time)).size()-1) + " INFO: " + message;
		this->writeToFile(finalMessage);
		std::cout << finalMessage << std::endl;
	}
}
void mylog::noteImpl(std::string message) {
	if(this->logLevel <= 1) {
		std::string finalMessage;
		auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
		finalMessage = std::string(std::ctime(&time)).substr(0, std::string(std::ctime(&time)).size()-1) + " NOTE: " + message;
		this->writeToFile(finalMessage);
		std::clog << finalMessage << std::endl;

	}
}
void mylog::warningImpl(std::string message) {
	if(this->logLevel <= 2) {
		std::string finalMessage;
		auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
		finalMessage = std::string(std::ctime(&time)).substr(0, std::string(std::ctime(&time)).size()-1) + " WARNING: " + message;
		this->writeToFile(finalMessage);
		std::cerr << finalMessage << std::endl;
	}
}
void mylog::errorImpl(std::string message, int errorCode) {
	std::string finalMessage;
	auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
	finalMessage = std::string(std::ctime(&time)).substr(0, std::string(std::ctime(&time)).size()-1) + " ERROR: " + message;
	this->writeToFile(finalMessage);
	std::cerr << finalMessage << std::endl;
	std::exit(errorCode);
}

bool mylog::removeLogFileImpl() {
	if(this->_logFile.is_open()) {
		this->_logFile.close();
	}
	if( remove(this->_logName.c_str()) != 0){
		std::cerr << "WARNING: cannot delete log file: " << this->_logName << std::endl;
		return false;
	}
	std::clog << "deleted log file: " << this->_logName << std::endl;
	return true;
	
}
void mylog::newLogFileImpl(std::string fileName) {
	//std::filesystem::path file { fileName };
	//if(std::filesystem::exists(file)) {
	this->_logName = fileName;
	if(this->_logFile.is_open()) {
		this->_logFile.close();
	}
	//} 
	//TODO: check if this is handled by the functions below
}

void mylog::changeLogLevelImpl(std::uint_fast8_t newLevel) {
	this->logLevel = newLevel;
}
int mylog::getLogLevelImpl() {
	return this->logLevel;
}
void mylog::writeToFile(std::string message) {
	if(this->testFile()) {
		this->_logFile << message << std::endl;
		//this->_logFile.close();
	} else {
		std::cerr << "WARNING: Can't create log file" << std::endl;	
		std::exit(-1);
	}
}
bool mylog::testFile() {
	std::filesystem::path file{ this->_logName };
	if(this->_logFile.is_open()){
		this->_logExist = true;
		return this->_logExist;
	} else if(this->_logFile.good()) {
		this->_logFile.open(this->_logName, std::ios::out | std::ios::app); 
		this->writeToFile("******************************************"); // This will also inject after the log file name is changed
		this->_logExist = true;
		return this->_logExist;
	} else if(!std::filesystem::exists(file)){
		this->_logFile.open(this->_logName, std::ios::out);
		this->writeToFile("******************************************");
		this->_logExist = true;
		return this->_logExist;
	}
	this->_logExist = false;
	return this->_logExist;
}
